/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include <assert.h>
#include "cfront.h"

#define WANT_FREE 0

/* struct s_void_cfront_TraverseTable check_original_tree; */
/* struct s_void_cfront_TraverseTable check_originals; */
struct s_void_cfront_TraverseTable check_tree;
struct s_cfront_TraverseTable      free_tree;
struct s_cfront_TraverseTable      free_bind;

struct s_cfront_TraverseTable      skip_table;
struct s_cfront_TraverseTable      propagate_srcinfo;

struct s_void_cfront_TraverseTable cfront_ErrorTable;
struct s_void_cfront_TraverseTable cfront_ClearErrorTable;
int cfront_ErrorFieldsOffset;
int cfront_SrcInfoOffset;


static unsigned int generation = 0;
Bool is_parsing = FALSE;
Bool skip_error_nodes = TRUE;

/* TODO:
   Instead of the assert (AST_node_mark(n) < generation) which detects
   sharing in the ast, we could make a deep copy of the shared node.
   This would remove the need to make copies for the application.
   But: what about the derived properties, like back-edges?
   The copy needs to be re-decorated. Or the original?
   Mmmmm, too fishy.
*/

static void mark_tree_action (AST_node n)
{
  if (n==NULL) return;
  assert (AST_node_mark(n) < generation);
#if WANT_ASSERT
  assert (!AST_node_free(n));
#endif
  AST_node_mark(n) = generation;
}

static Bool free_tree_action (AST_node n, Bool free)
{
  if (n==NULL) return free;
  if (AST_node_mark(n) >= generation) {
#if 0
    assert (!free); /* If n is reachable, so should be all its children */
#endif
    return free;
  }
  AST_node_free(n) = TRUE;
#if WANT_FREE
  Front_Free(n);
#endif
  return TRUE;
}

/* static void */
/* check_original_action(AST_node n) */
/* { */
/*   if(!n) return; */
/*   assert (AST_node_mark(n) < generation); */
/* #if WANT_ASSERT */
/*   assert (!AST_node_free(n)); */
/* #endif */
/* } */

static void *fill_check_tree (Bool e_num, Bool sub, Bool list, Bool opt, Bool memo, Int action)
{
  if (e_num || sub || list || opt || memo) return NULL;
  if (action == PRE_ACTION) return mark_tree_action;
  else return NULL;
}

/* static void * */
/* fill_check_original_tree (Bool e_num, Bool sub, Bool list, Bool opt, Bool memo, Int action) */
/* { */
/*   if (e_num || sub || list || opt || memo) return NULL; */
/*   if (action == PRE_ACTION) return check_original_action; */
/*   else return NULL; */
/* } */

static void *
fill_free_tree (Bool e_num, Bool sub, Bool list, Bool opt, Bool memo, Int action)
{
  if (e_num || sub || list || opt || memo) return NULL;
  if (action == POST_ACTION) return free_tree_action;
  else return NULL;
}

/*********/

static void *free_label (statement me, void *val)
{
  if (me==NULL) return val;
  if (AST_node_mark(me) >= generation) return val;
  switch (statement_tag(me)) {
    case LabeledStat_kind:
      Remove_Bind (LabeledStat_label_bind(me), cfront_LabelNameSpace);
      LabeledStat_label_bind(me) = NULL;
      break;
    default:
      break;
  }
  return val;
}

static void *free_decl_spec (declaration_specifier me, void *val)
{
  if (me==NULL) return val;
  if (AST_node_mark(me) >= generation) return val;
  switch (declaration_specifier_tag(me)) {
  case Enum_kind:
    Remove_Bind (Enum_id_bind(me), cfront_TagNameSpace);
    Enum_id_bind(me) = NULL;
    break;
  case StructDef_kind:
    Remove_Bind (StructDef_id_bind(me), cfront_TagNameSpace);
    StructDef_id_bind(me) = NULL;
    break;
  case StructDecl_kind:
    Remove_Bind (StructDecl_id_bind(me), cfront_TagNameSpace);
    StructDecl_id_bind(me) = NULL;
    break;
  case UnionDef_kind:
    Remove_Bind (UnionDef_id_bind(me), cfront_TagNameSpace);
    UnionDef_id_bind(me) = NULL;
    break;
  case UnionDecl_kind:
    Remove_Bind (UnionDecl_id_bind(me), cfront_TagNameSpace);
    UnionDecl_id_bind(me) = NULL;
    break;
  default:
    break;
  }
  return val;
}

static void *free_declarator (declarator me, void *val)
{
  if (me==NULL) return val;
  if (AST_node_mark(me) >= generation) return val;
  switch (declarator_tag(me)) {
    case enumerator_kind:
      Remove_Bind (enumerator_id_bind(me), cfront_DefaultNameSpace);
      enumerator_id_bind(me) = NULL;
      break;
    case Id_Decl_kind:
      Remove_Bind (Id_Decl_id_bind(me), cfront_DefaultNameSpace);
      Id_Decl_id_bind(me) = NULL;
      break;
    case Par_Id_Decl_kind:
      Remove_Bind (Par_Id_Decl_id_bind(me), cfront_DefaultNameSpace);
      Par_Id_Decl_id_bind(me) = NULL;
      break;
    default:
      break;
  }
  return val;
}

static void fill_free_bind (void)
{  
  free_bind.action_LabeledStat[PRE_ACTION] = free_label;
  free_bind.action_declaration_specifier[PRE_ACTION] = free_decl_spec;
  free_bind.action_declarator[PRE_ACTION] = free_declarator;
}

/* static void */
/* expr_check_original(expression expr) */
/* { */
/*   static Bool filled = FALSE; */
  
/*   if (!filled) { */
/*     void_cfront_fill_table (&check_original_tree, fill_check_original_tree); */
/*     filled = TRUE; */
/*   } */
  
/*   if(!expr || !expression_original(expr)) return; */
/*   assert ( expression_original(expression_original(expr))==NULL ); */
/*   Traverse_void_expression (expression_original(expr), &check_original_tree); */
/* } */

/*********/

void Mark_AST_node (void * node, unsigned int gen, 
                void (*traverse) (void *me, void_cfront_TraverseTable table)
		)
{ static Bool filled = FALSE;
  unsigned int g = generation;
  
  if (!filled) {
    void_cfront_fill_table (&check_tree, fill_check_tree);
/*     check_originals.action_expression[PRE_ACTION] = expr_check_original; */
    filled = TRUE;
  }
  
  generation = gen;
  traverse (node, &check_tree);
/*   traverse (node, &check_originals); */
  generation = g;
}


void Check_AST_node (void * node,
                void (*traverse) (void *me, void_cfront_TraverseTable table)
		)
{ generation++;
  Mark_AST_node (node, generation, traverse);
}

void Free_AST_node (void * node,
                Bool (*traverse) (void *me, cfront_TraverseTable table, Bool free)
		)
{ static Bool filled = FALSE;
  
  if (!filled) {
    cfront_fill_table (&free_tree, fill_free_tree);
    fill_free_bind ();
    filled = TRUE;
  }
  
  /* TODO: Traverse again and for each freed defining occurrence, remove it from the symbol-table
  */
  traverse (node, &free_bind, FALSE);
  traverse (node, &free_tree, FALSE);
}

void Mark_node (AST_node n)
{
  AST_node_mark(n) = generation;
}

void Check_AST (void)
{
  Check_AST_node (cfront_root, (void*)Traverse_void_cfront);
}

void Free_AST (void)
{
  Free_AST_node (cfront_root, (void*)Traverse_cfront);
}

#if 0
int
_cfront_print_errors (void *n, void trav_void())
{
  int v;
  skip_error_nodes = FALSE;
  v = _AST_print_errors(n,&cfront_ErrorTable,(void*)trav_void,
                        cfront_ErrorFieldsOffset,
                        cfront_SrcInfoOffset);
  skip_error_nodes = TRUE;
  return v;
}

void
_cfront_clear_errors (void * n, void trav_void())
{
  skip_error_nodes = FALSE;
  _AST_clear_errors(n,&cfront_ErrorTable,(void*)trav_void,
                    cfront_ErrorFieldsOffset);
  skip_error_nodes = TRUE;
}
#endif

void
inherit_error (void *a, void *b)
{
  AST_node x=a, y=b;
  if (CAST_error(y)) CAST_copy_error (x, y);
}

void
withdraw_error (void *n)
{
  AST_node node = n;
  egg_withdraw (_AST_message (node));
  _AST_message (node) = NULL;
}


static Bool
skip_node (AST_node node)
{
  if (node && _AST_message(node) && _AST_message(node)->withdrawn) {
    _AST_message(node) = NULL;
  }
  return node && CAST_error (node);
}

static void *
fill_skip_table (Bool e_num, Bool sub, Bool list, Bool opt, Bool memo, Int action)
{
  if (e_num || sub || list || opt || memo) return NULL;
  if (action == 1) return skip_node;
  else return NULL;
}

void
init_skip_table (void)
{
  cfront_fill_table (&skip_table, fill_skip_table);
}

static SrcInfo
Node_propagate_srcinfo (AST_node node, SrcInfo si)
{
  if (!node) return si;
  AST_node_src_info(node) = si;
  return si;
}

static void *
fill_propagate_table (Bool e_num, Bool sub, Bool list, Bool opt, Bool memo, Int action)
{
  if (e_num || sub || list || opt || memo) return NULL;
  if (action == 1) return Node_propagate_srcinfo;
  else return NULL;
}

void
_ast_propagate_srcinfo (AST_node node, SrcInfo si, void *trav())
{
  static Bool init=FALSE;
  if (!init) {
    cfront_fill_table (&propagate_srcinfo, fill_propagate_table);
    init = TRUE;
  }
  trav(node, &propagate_srcinfo, si);
}
